#include "DetatchPanel.hpp"
#include "MeshAdjacency.hpp"
#include "MeshAlgorithm.hpp"
#include "MeshDraw.hpp"
#include "MeshView.hpp"
#include "ViewScene.hpp"
#include "Panel.hpp"
#include <boost/lexical_cast.hpp>
using namespace MeshAlgorithm;

namespace
{
   DetatchPanel* panelPtr = 0;
   void FillSelect(Fl_Widget* aWidget,
                   void*      aData)
   {
      panelPtr->FillSelected();
   }
   void OkButton(Fl_Widget* aWidget,
                 void*      aData)
   {
      panelPtr->Execute();      
   }
}

DetatchPanel::DetatchPanel()
{
   panelPtr = this;
   mDetatchDlg = new DetatchInterfaceDlg;
   mDetatchDlg->make_window();
   mDetatchDlg->mFillList->callback(FillSelect);
   mDetatchDlg->mOkButton->callback(OkButton);
}
void DetatchPanel::BeginEdges()
{
   MeshView* meshPtr = ViewScene::GetInstance().GetSelectedMesh();
   if (!meshPtr) return;
   if (meshPtr->mSelectedEdges.empty()) return;
   FaceList& meshFaces = *meshPtr->mMesh.crease_faces;
   MeshAdjacency& adj = meshPtr->GetMeshAdjacency();
   EdgeList& edges = meshPtr->mSelectedEdges;
   std::set<int> fillMats;
   for (size_t i = 0; i < edges.size(); ++i)
   {
      Edge& e = edges[i];
      int faces[128];
      int fCount = adj.GetEdgeFaces(e, faces);
      for (size_t j = 0; j < fCount; ++j)
      {
         fillMats.insert(meshFaces[faces[j]].materials[0]);
         fillMats.insert(meshFaces[faces[j]].materials[1]);
      }
   }
   mFillChoices.assign(fillMats.begin(), fillMats.end());
}

void DetatchPanel::BeginFaces()
{
   MeshView* meshPtr = ViewScene::GetInstance().GetSelectedMesh();
   if (!meshPtr) return;
   IndexList selFaces = meshPtr->GetSelectedFaces();
   if (selFaces.empty()) return;
   std::sort(selFaces.begin(), selFaces.end());
   MeshAdjacency& adjacency = meshPtr->GetMeshAdjacency();
   Mesh& mesh = adjacency.GetMesh();
   FaceList& faces = *mesh.faces;
   // Figure out fill choices
   EdgeList borderEdges;
   IndexList borderVertices, borderFaces;
   MeshAlgorithm::GetBorderEdges(mesh, selFaces, borderEdges);
   MeshAlgorithm::GetEdgeVertices(borderEdges, borderVertices);
   adjacency.GetVertexAdjacentFaces(borderVertices, borderFaces);
   RegionInterface selRegion = face_region(faces[selFaces[0]]);
   SetRegionColor(selRegion);
   std::set<int> selFaceSet(selFaces.begin(), selFaces.end());
   std::set<int> borderFaceSet(borderFaces.begin(), borderFaces.end());
   IndexList realBorderFaces;
   std::set_difference(borderFaceSet.begin(), borderFaceSet.end(), selFaces.begin(), selFaces.end(), std::back_inserter(realBorderFaces));
   std::set<int> fillMats;
   for (IndexList::iterator i = realBorderFaces.begin(); i != realBorderFaces.end(); ++i)
   {
      fillMats.insert(faces[*i].materials[0]);
      fillMats.insert(faces[*i].materials[1]);
   }
   fillMats.erase(selRegion.first);
   fillMats.erase(selRegion.second);
   mFillChoices.assign(fillMats.begin(), fillMats.end());
}

void DetatchPanel::Begin()
{
   mFillChoices.clear();
   mFillChoice = -1;
   if (ViewScene::GetInstance().GetSelectionMode() != ViewScene::cEDGE_PATCH)
   {
      BeginFaces();
   }
   else
   {
      BeginEdges();
   }
   mDetatchDlg->mFillList->clear();
   for (size_t i = 0; i < mFillChoices.size(); ++i)
   {
      mDetatchDlg->mFillList->add(ColorCheckBrowser::RegionLabel(mFillChoices[i]).c_str(), 0);
   }

   mDetatchDlg->mFillList->callback();
   mDetatchDlg->mWindow->show();
}

void DetatchPanel::SetRegionColor(RegionInterface& aInterface)
{
   Fl_Box* widgets[] = { mDetatchDlg->mInterfaceA, mDetatchDlg->mInterfaceB };
   Color c1 = MeshDraw::GetMaterialColor(aInterface.first);
   Color c2 = MeshDraw::GetMaterialColor(aInterface.second);
   Fl_Box** w = widgets;
   
   w[0]->copy_label(boost::lexical_cast<std::string>(aInterface.first).c_str());
   w[1]->copy_label(boost::lexical_cast<std::string>(aInterface.second).c_str());
   w[0]->color(fl_rgb_color(c1.r * 255, c1.g * 255, c1.b*255));
   w[1]->color(fl_rgb_color(c2.r * 255, c2.g * 255, c2.b*255));
   w[0]->redraw();
   w[1]->redraw();

}

void DetatchPanel::FillSelected()
{
   MeshView* meshPtr = ViewScene::GetInstance().GetSelectedMesh();
   if (!meshPtr) return;
   mFillChoice = mDetatchDlg->mFillList->value()-1;
}

void DetatchPanel::Execute()
{
   MeshView* meshPtr = ViewScene::GetInstance().GetSelectedMesh();
   if (!meshPtr) return;
   MeshAdjacency& adj = meshPtr->GetMeshAdjacency();
   if (mFillChoice < mFillChoices.size())
   {
      int fillMat = mFillChoices[mFillChoice];
      const IndexList& selFaces = meshPtr->GetSelectedFaces();
      EdgeList edges;
      if (ViewScene::GetInstance().GetSelectionMode() == ViewScene::cEDGE_PATCH)
      {
         edges = meshPtr->mSelectedEdges;
      }
      MeshAlgorithm::DetatchInterface4(meshPtr->mMesh.GetCreaseMesh(), adj, selFaces, edges, fillMat);
      meshPtr->Rebuild();
      MEvents("Redraw");
      mDetatchDlg->mWindow->hide();
   }
}